using System;
using System.Collections;
using Evaluant.Uss.Collections;

namespace Evaluant.Uss
{
	/// <summary>
	///     <para>
	///       A collection that stores <see cref='Entity'/> objects.
	///    </para>
	/// </summary>
	/// <seealso cref='EntitySet'/>
	[Serializable()]
	public class EntitySet : HashedList	
	{
		
		/// <summary>
		///     <para>
		///       Initializes a new instance of <see cref='EntitySet'/>.
		///    </para>
		/// </summary>
		public EntitySet()
		{
		}
		
		/// <summary>
		///     <para>
		///       Initializes a new instance of <see cref='EntitySet'/> based on another <see cref='EntitySet'/>.
		///    </para>
		/// </summary>
		/// <param name='val'>
		///       A <see cref='EntitySet'/> from which the contents are copied
		/// </param>
		public EntitySet(EntitySet val)
		{
			this.AddRange(val);
		}
		
		/// <summary>
		///     <para>
		///       Initializes a new instance of <see cref='EntitySet'/> containing any array of <see cref='Entity'/> objects.
		///    </para>
		/// </summary>
		/// <param name='val'>
		///       A array of <see cref='Entity'/> objects with which to intialize the collection
		/// </param>
		public EntitySet(Entity[] val)
		{
			this.AddRange(val);
		}
		
		/// <summary>
		/// <para>Represents the entry at the specified index of the <see cref='Entity'/>.</para>
		/// </summary>
		/// <param name='index'><para>The zero-based index of the entry to locate in the collection.</para></param>
		/// <value>
		///    <para> The entry at the specified index of the collection.</para>
		/// </value>
		/// <exception cref='System.ArgumentOutOfRangeException'><paramref name='index'/> is outside the valid range of indexes for the collection.</exception>
		public new Entity this[int index] 
		{
			get 
			{
				return ((Entity)(base[index]));
			}
			set 
			{
				base[index] = value;
			}
		}
		
		/// <summary>
		///    <para>Adds a <see cref='Entity'/> with the specified value to the 
		///    <see cref='EntitySet'/> .</para>
		/// </summary>
		/// <param name='val'>The <see cref='Entity'/> to add.</param>
		/// <returns>
		///    <para>The index at which the new element was inserted.</para>
		/// </returns>
		/// <remarks>The same already in the list can't be added</remarks>
		/// <seealso cref='EntitySet.AddRange'/>
		public int Add(Entity val)
		{
			return base.Add(val);
		}
		
		/// <summary>
		/// <para>Copies the elements of an array to the end of the <see cref='EntitySet'/>.</para>
		/// </summary>
		/// <param name='val'>
		///    An array of type <see cref='Entity'/> containing the objects to add to the collection.
		/// </param>
		/// <returns>
		///   <para>None.</para>
		/// </returns>
		/// <seealso cref='EntitySet.Add'/>
		public void AddRange(Entity[] val)
		{
			for (int i = 0; i < val.Length; i++) 
			{
				this.Add(val[i]);
			}
		}
		
		/// <summary>
		///     <para>
		///       Adds the contents of another <see cref='EntitySet'/> to the end of the collection.
		///    </para>
		/// </summary>
		/// <param name='val'>
		///    A <see cref='EntitySet'/> containing the objects to add to the collection.
		/// </param>
		/// <returns>
		///   <para>None.</para>
		/// </returns>
		/// <seealso cref='EntitySet.Add'/>
		public void AddRange(EntitySet val)
		{
			for (int i = 0; i < val.Count; i++)
			{
				this.Add(val[i]);
			}
		}
		
		/// <summary>
		/// <para>Gets a value indicating whether the 
		///    <see cref='EntitySet'/> contains the specified <see cref='Entity'/>.</para>
		/// </summary>
		/// <param name='val'>The <see cref='Entity'/> to locate.</param>
		/// <returns>
		/// <para><see langword='true'/> if the <see cref='Entity'/> is contained in the collection; 
		///   otherwise, <see langword='false'/>.</para>
		/// </returns>
		/// <seealso cref='EntitySet.IndexOf'/>
		public bool Contains(Entity val)
		{
			return base.Contains(val);
		}
		
		/// <summary>
		/// <para>Copies the <see cref='EntitySet'/> values to a one-dimensional <see cref='System.Array'/> instance at the 
		///    specified index.</para>
		/// </summary>
		/// <param name='array'><para>The one-dimensional <see cref='System.Array'/> that is the destination of the values copied from <see cref='EntitySet'/> .</para></param>
		/// <param name='index'>The index in <paramref name='array'/> where copying begins.</param>
		/// <returns>
		///   <para>None.</para>
		/// </returns>
		/// <exception cref='System.ArgumentException'><para><paramref name='array'/> is multidimensional.</para> <para>-or-</para> <para>The number of elements in the <see cref='EntitySet'/> is greater than the available space between <paramref name='index'/> and the end of <paramref name='array'/>.</para></exception>
		/// <exception cref='System.ArgumentNullException'><paramref name='array'/> is <see langword='null'/>. </exception>
		/// <exception cref='System.ArgumentOutOfRangeException'><paramref name='index'/> is less than <paramref name='array'/>'s lowbound. </exception>
		/// <seealso cref='System.Array'/>
		public void CopyTo(Entity[] array, int index)
		{
			base.CopyTo(array, index);
		}
		
		/// <summary>
		///    <para>Returns the index of a <see cref='Entity'/> in 
		///       the <see cref='EntitySet'/> .</para>
		/// </summary>
		/// <param name='val'>The <see cref='Entity'/> to locate.</param>
		/// <returns>
		/// <para>The index of the <see cref='Entity'/> of <paramref name='val'/> in the 
		/// <see cref='EntitySet'/>, if found; otherwise, -1.</para>
		/// </returns>
		/// <seealso cref='EntitySet.Contains'/>
		public int IndexOf(Entity val)
		{
			return base.IndexOf(val);
		}
		
		/// <summary>
		/// <para>Inserts a <see cref='Entity'/> into the <see cref='EntitySet'/> at the specified index.</para>
		/// </summary>
		/// <param name='index'>The zero-based index where <paramref name='val'/> should be inserted.</param>
		/// <param name='val'>The <see cref='Entity'/> to insert.</param>
		/// <returns><para>None.</para></returns>
		/// <seealso cref='EntitySet.Add'/>
		public void Insert(int index, Entity val)
		{
			base.Insert(index, val);
		}
		
		/// <summary>
		///    <para>Returns an enumerator that can iterate through 
		///       the <see cref='EntitySet'/> .</para>
		/// </summary>
		/// <returns><para>None.</para></returns>
		/// <seealso cref='System.Collections.IEnumerator'/>
		public new EntityEnumerator GetEnumerator()
		{
			return new EntityEnumerator(this);
		}
		
		/// <summary>
		///    <para> Removes a specific <see cref='Entity'/> from the 
		///    <see cref='EntitySet'/> .</para>
		/// </summary>
		/// <param name='val'>The <see cref='Entity'/> to remove from the <see cref='EntitySet'/> .</param>
		/// <returns><para>None.</para></returns>
		/// <exception cref='System.ArgumentException'><paramref name='val'/> is not found in the Collection. </exception>
		public void Remove(Entity val)
		{
			base.Remove(val);
		}

		/// <summary>
		/// Sorts its elements.
		/// </summary>
		/// <remarks>The value to use as the sort criterium must be loaded in the object graph</remarks>
		/// <param name="path">Path.</param>
		public void Sort(string path)
		{
            Sort(new string[] { path });
		}

		/// <summary>
		/// Sorts its elements.
		/// </summary>
		/// <remarks>The value to use as the sort criterium must be loaded in the object graph</remarks>
		/// <param name="path">Path.</param>
		/// <param name="ascending">False to sort in a reverse order.</param>
		public void Sort(string path, bool ascending)
		{
			ArrayList sortedList = new ArrayList(base.Count);
			sortedList.AddRange(this);
			sortedList.Sort(new NavigationComparer(path, ascending));

			base.Clear();
			for(int i=0; i< sortedList.Count; i++)
				base.Add(sortedList[i]);
		}

		/// <summary>
		/// Sorts its elements.
		/// </summary>
		/// <remarks>The value to use as the sort criterium must be loaded in the object graph</remarks>
		/// <param name="path">Path.</param>
		/// <param name="ascending">False to sort in a reverse order.</param>
		public void Sort(string[] path)
		{
            ArrayList directions = new ArrayList(path.Length);
            
            for (int i = 0; i < path.Length; i++)
            {
                string[] values = path[i].Trim().Split();
                bool ascending = true;

                if (values.Length > 2)
                    throw new ArgumentException("path[" + i + "]");

                if (values.Length == 2)
                {
                    ascending = (values[1] == "asc");
                    path[i] = values[0];
                }
                directions.Add(ascending);
            }

			ArrayList sortedList = new ArrayList(base.Count);
			sortedList.AddRange(this);
			sortedList.Sort(new NavigationComparer(path, (bool[])directions.ToArray(typeof(bool))));

			base.Clear();
			for(int i=0; i< sortedList.Count; i++)
				base.Add(sortedList[i]);
		}

        /// <summary>
        /// Copies the elements of the EntitySet to a new Entity array.
        /// </summary>
        /// <returns>An Entity array containing copies of the elements of the EntitySet. </returns>
        public Entity[] ToArray()
        {
            Entity[] entities = new Entity[this.Count];
            this.CopyTo(entities, 0);

            return entities;
        }

		/// <summary>
		/// Gets the changes.
		/// </summary>
		/// <returns>A subset of the current EntitySet containing only modified items</returns>
		public CommandCollection GetChanges()
		{
			CommandCollection commands = new CommandCollection();
			
			foreach(Entity e in this)
				commands.AddRange(e.GetChanges());

			return commands;
		}

		/// <summary>
		/// Clones this instance.
		/// </summary>
		/// <returns></returns>
		public EntitySet Clone()
		{
			EntitySet es = new EntitySet();
	
			foreach(Entity e in this)
				es.Add(e.Clone());

			return es;
		}

		public class EntityEnumerator : IEnumerator
		{
			IEnumerator baseEnumerator;
			IEnumerable temp;
			
			public EntityEnumerator(EntitySet mappings)
			{
				this.temp = mappings;
				this.baseEnumerator = temp.GetEnumerator();
			}
			
			public Entity Current 
			{
				get 
				{
					return ((Entity)(baseEnumerator.Current));
				}
			}
			
			object IEnumerator.Current 
			{
				get 
				{
					return baseEnumerator.Current;
				}
			}
			
			public bool MoveNext()
			{
				return baseEnumerator.MoveNext();
			}
			
			bool IEnumerator.MoveNext()
			{
				return baseEnumerator.MoveNext();
			}
			
			public void Reset()
			{
				baseEnumerator.Reset();
			}
			
			void IEnumerator.Reset()
			{
				baseEnumerator.Reset();
			}
		}

        public override string ToString()
        {
            return this.Count.ToString();
        }
	}
}
